/*
 * ProGuard -- shrinking, optimization, obfuscation, and preverification of Java bytecode.
 *
 * Copyright (c) 2002-2018 GuardSquare NV
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package proguard.ant;

import org.apache.tools.ant.BuildException;
import proguard.*;
import proguard.classfile.util.ClassUtil;

import java.io.*;
import java.net.*;
import java.util.*;

/**
 * This Task allows to configure and run ProGuard from Ant.
 *
 * @author Eric Lafortune
 */
public class ProGuardTask extends ConfigurationTask {
    // Ant task attributes.

    public void setConfiguration(File configurationFile) throws BuildException {
        try {
            // Get the combined system properties and Ant properties, for
            // replacing ProGuard-style properties ('<...>').
            Properties properties = new Properties();
            properties.putAll(getProject().getProperties());

            URL configUrl = ConfigurationElement.class.getResource(configurationFile.toString());

            ConfigurationParser parser = configUrl != null ? new ConfigurationParser(configUrl, properties)
                : new ConfigurationParser(configurationFile, properties);

            try {
                parser.parse(configuration);
            } catch (ParseException e) {
                throw new BuildException(e.getMessage(), e);
            } finally {
                parser.close();
            }
        } catch (IOException e) {
            throw new BuildException(e.getMessage(), e);
        }
    }

    /**
     * @deprecated Use the nested outjar element instead.
     */
    public void setOutjar(String parameters) {
        throw new BuildException("Use the <outjar> nested element instead of the 'outjar' attribute");
    }

    public void setSkipnonpubliclibraryclasses(boolean skipNonPublicLibraryClasses) {
        configuration.skipNonPublicLibraryClasses = skipNonPublicLibraryClasses;
    }

    public void setSkipnonpubliclibraryclassmembers(boolean skipNonPublicLibraryClassMembers) {
        configuration.skipNonPublicLibraryClassMembers = skipNonPublicLibraryClassMembers;
    }

    public void setTarget(String target) {
        configuration.targetClassVersion = ClassUtil.internalClassVersion(target);
        if (configuration.targetClassVersion == 0) {
            throw new BuildException("Unsupported target '" + target + "'");
        }
    }

    public void setForceprocessing(boolean forceProcessing) {
        configuration.lastModified = forceProcessing ? Long.MAX_VALUE : 0;
    }

    public void setPrintseeds(File printSeeds) {
        configuration.printSeeds = optionalFile(printSeeds);
    }

    public void setShrink(boolean shrink) {
        configuration.shrink = shrink;
    }

    public void setPrintusage(File printUsage) {
        configuration.printUsage = optionalFile(printUsage);
    }

    public void setOptimize(boolean optimize) {
        configuration.optimize = optimize;
    }

    public void setOptimizationpasses(int optimizationPasses) {
        configuration.optimizationPasses = optimizationPasses;
    }

    public void setAllowaccessmodification(boolean allowAccessModification) {
        configuration.allowAccessModification = allowAccessModification;
    }

    public void setMergeinterfacesaggressively(boolean mergeinterfacesaggressively) {
        configuration.mergeInterfacesAggressively = mergeinterfacesaggressively;
    }

    public void setObfuscate(boolean obfuscate) {
        configuration.obfuscate = obfuscate;
    }

    public void setPrintmapping(File printMapping) {
        configuration.printMapping = optionalFile(printMapping);
    }

    public void setApplymapping(File applyMapping) {
        configuration.applyMapping = resolvedFile(applyMapping);
    }

    public void setObfuscationdictionary(File obfuscationDictionary) {
        configuration.obfuscationDictionary = resolvedURL(obfuscationDictionary);
    }

    public void setClassobfuscationdictionary(File classObfuscationDictionary) {
        configuration.classObfuscationDictionary = resolvedURL(classObfuscationDictionary);
    }

    public void setPackageobfuscationdictionary(File packageObfuscationDictionary) {
        configuration.packageObfuscationDictionary = resolvedURL(packageObfuscationDictionary);
    }

    public void setOverloadaggressively(boolean overloadAggressively) {
        configuration.overloadAggressively = overloadAggressively;
    }

    public void setUseuniqueclassmembernames(boolean useUniqueClassMemberNames) {
        configuration.useUniqueClassMemberNames = useUniqueClassMemberNames;
    }

    public void setUsemixedcaseclassnames(boolean useMixedCaseClassNames) {
        configuration.useMixedCaseClassNames = useMixedCaseClassNames;
    }

    public void setFlattenpackagehierarchy(String flattenPackageHierarchy) {
        configuration.flattenPackageHierarchy = ClassUtil.internalClassName(flattenPackageHierarchy);
    }

    public void setRepackageclasses(String repackageClasses) {
        configuration.repackageClasses = ClassUtil.internalClassName(repackageClasses);
    }

    /**
     * @deprecated Use the repackageclasses attribute instead.
     */
    public void setDefaultpackage(String defaultPackage) {
        configuration.repackageClasses = ClassUtil.internalClassName(defaultPackage);
    }

    public void setKeepparameternames(boolean keepParameterNames) {
        configuration.keepParameterNames = keepParameterNames;
    }

    public void setRenamesourcefileattribute(String newSourceFileAttribute) {
        configuration.newSourceFileAttribute = newSourceFileAttribute;
    }

    public void setPreverify(boolean preverify) {
        configuration.preverify = preverify;
    }

    public void setMicroedition(boolean microEdition) {
        configuration.microEdition = microEdition;
    }

    public void setAndroid(boolean android) {
        configuration.android = android;
    }

    public void setVerbose(boolean verbose) {
        configuration.verbose = verbose;
    }

    public void setNote(boolean note) {
        if (note) {
            // Switch on notes if they were completely disabled.
            if (configuration.note != null && configuration.note.isEmpty()) {
                configuration.note = null;
            }
        } else {
            // Switch off notes.
            configuration.note = new ArrayList();
        }
    }

    public void setWarn(boolean warn) {
        if (warn) {
            // Switch on warnings if they were completely disabled.
            if (configuration.warn != null && configuration.warn.isEmpty()) {
                configuration.warn = null;
            }
        } else {
            // Switch off warnings.
            configuration.warn = new ArrayList();
        }
    }

    public void setIgnorewarnings(boolean ignoreWarnings) {
        configuration.ignoreWarnings = ignoreWarnings;
    }

    public void setPrintconfiguration(File printConfiguration) {
        configuration.printConfiguration = optionalFile(printConfiguration);
    }

    public void setDump(File dump) {
        configuration.dump = optionalFile(dump);
    }

    public void setAddConfigurationDebugging(boolean addConfigurationDebugging) {
        configuration.addConfigurationDebugging = addConfigurationDebugging;
    }

    // Implementations for Task.

    public void execute() throws BuildException {
        try {
            ProGuard proGuard = new ProGuard(configuration);
            proGuard.execute();
        } catch (IOException e) {
            throw new BuildException(e.getMessage(), e);
        }
    }

    // Small utility methods.

    /**
     * Returns a file that is properly resolved with respect to the project directory, or <code>null</code> or empty if
     * its name is actually a boolean flag.
     */
    private File optionalFile(File file) {
        String fileName = file.getName();

        return fileName.equalsIgnoreCase("false") || fileName.equalsIgnoreCase("no") || fileName.equalsIgnoreCase("off")
            ? null
            : fileName.equalsIgnoreCase("true") || fileName.equalsIgnoreCase("yes") || fileName.equalsIgnoreCase("on")
                ? Configuration.STD_OUT : resolvedFile(file);
    }

    /**
     * Returns a URL that is properly resolved with respect to the project directory.
     */
    private URL resolvedURL(File file) {
        try {
            return resolvedFile(file).toURI().toURL();
        } catch (MalformedURLException e) {
            return null;
        }
    }

    /**
     * Returns a file that is properly resolved with respect to the project directory.
     */
    private File resolvedFile(File file) {
        return file.isAbsolute() ? file : new File(getProject().getBaseDir(), file.getName());
    }
}
